/**   
 * @Title: HttpClientUtil.java 
 * @Package com.lencee.sso.utils 
 * @Description: 
 * @author liugang   
 * @date 2014-6-23 下午3:18:38   
 */
package com.angeho.util;


import cn.hutool.core.util.IdUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * 
 * <p>
 * HttpClient工具类
 * <p>
 * <p>
 * 实现基本的表单提交操作
 * </p>
 * 
 * @version 2014年12月8日 下午3:55:58
 * 
 */
public class HttpClientUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtil.class);
	//  客户端从服务端读取数据的超时时间
	private static final int HTTP_TIMEOUT = 15000;
	//  路由的默认最大连接
	private static final int HTTP_MAX_PERROUTE = 500;
	//  整个连接池连接的最大值
	private static final int HTTP_MAX_TOTAL = 1000;

	private static CloseableHttpClient httpClient = getClientNoSSL();

	private static CloseableHttpClient getClientNoSSL() {
		try {
			X509TrustManager trustManager = new X509TrustManager() {
				@Override public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
				@Override public void checkClientTrusted(X509Certificate[] xcs, String str) {}
				@Override public void checkServerTrusted(X509Certificate[] xcs, String str) {}
			};
			SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
			ctx.init(null, new TrustManager[] { trustManager }, null);
			SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
			//  创建Registry
			RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)// 获取连接超时时间
					.setConnectionRequestTimeout(3000)
					// 请求超时时间
					.setConnectTimeout(HTTP_TIMEOUT)
					// 响应超时时间
					.setSocketTimeout(HTTP_TIMEOUT)
					.setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM,AuthSchemes.DIGEST))
					.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).build();
			Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
					.register("http", PlainConnectionSocketFactory.INSTANCE)
					.register("https",socketFactory).build();
			// 创建ConnectionManager，添加Connection配置信息
			PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
			connectionManager.setMaxTotal(HTTP_MAX_TOTAL);
			connectionManager.setDefaultMaxPerRoute(HTTP_MAX_PERROUTE);
			return HttpClients.custom().setConnectionManager(connectionManager)
					.setDefaultRequestConfig(requestConfig).build();
		} catch (KeyManagementException | NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 用于追踪报文的头部参数
	 */
	private static final String HEADER_TRACEID = "traceId";
	/**
	 * 请求默认编码方式
	 */
	private static final String DEFAULT_CHARSET = "utf-8";
	/**
	 * 使用Get方式调用HTTP请求
	 * 
	 * @param url
	 *            请求地址的URL
	 * @param paramMap
	 *            请求的参数列表，必须转化为字符串的键值对
	 * @return 返回响应的结果字符串
	 * @since 1.0.0
	 */
	public static String get(String url, Map<String, String> paramMap) {
		return get(url, paramMap, null);
	}
	/**
	 * 使用Get方式调用HTTP请求
	 *
	 * @param url 请求地址的URL
	 * @return 返回响应的结果字符串
	 * @since 1.0.0
	 */
	public static String get(String url) {
		return get(url, null, null);
	}

	/**
	 * 使用Get方式调用HTTP请求
	 *
	 * @param url 请求地址的URL
	 * @param paramMap 请求的参数列表，必须转化为字符串的键值对
	 * @param headerMap 请求头参数
	 * @return 返回响应的结果字符串
	 * @since 1.0.0
	 */
	public static String get(String url, Map<String, String> paramMap, Map<String, String> headerMap) {
		String result = null;
		CloseableHttpResponse httpResp = null;
		try {
			long startTime = System.currentTimeMillis();
			url += joinString(paramMap);
			HttpGet httpGet = new HttpGet(url);
			setRequestHeader(headerMap, httpGet);
			if(LOGGER.isInfoEnabled()) {
				LOGGER.info("[GET] 发起请求[{}]到[{}], 请求头:{}, 请求参数:{},：{}", httpGet.getHeaders(HEADER_TRACEID), url, headerMap, paramMap);
			}
			httpResp = httpClient.execute(httpGet);

			int statusCode = httpResp.getStatusLine().getStatusCode();
			if (statusCode == HttpStatus.SC_OK) {
				result = EntityUtils.toString(httpResp.getEntity(), DEFAULT_CHARSET);
				LOGGER.info("[GET] 请求[{}]成功！用时:{} 毫秒, 返回结果：{}", httpGet.getHeaders(HEADER_TRACEID), (System.currentTimeMillis()-startTime), result);
			} else {
				LOGGER.info("[GET] 请求[{}]失败！状态码:{}", httpGet.getHeaders(HEADER_TRACEID), httpResp.getStatusLine().getStatusCode());
			}
		} catch(Exception e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			try {
				if(httpResp != null) {
					httpResp.close();
				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			}
		}
		return result;

	}

	/**
	 * 将map内的参数拼接成string
	 * @param paramMap hash参数
	 * @return hash加工为字符串
	 */
	private static String joinString(Map<String, String> paramMap) {
		StringBuilder queryString = new StringBuilder();
		if (null != paramMap && paramMap.size() > 0) {
			queryString.append("?1=1");
			Set<String> keys = paramMap.keySet();
			for (String key : keys) {
				if (StringUtils.isNotBlank(key)) {
					String value = paramMap.get(key);
					queryString.append("&").append(key).append("=").append(value);
				}
			}
		}
		if(LOGGER.isDebugEnabled()) {
			LOGGER.debug(queryString.toString());
		}
		return queryString.toString();
	}

	/**
	 * 设置请求头参数
	 * @param headerMap Map类型的参数
	 * @param httpMethod HttpRequestBase对象，为HttpPost和HttpGet的父类
	 */
	private static void setRequestHeader(Map<String, String> headerMap, HttpRequestBase httpMethod) {
		// 封装请求头参数
		if (null != headerMap) {
			Set<String> headerNames = headerMap.keySet();
			for (String headerName : headerNames) {
				String headerValue = headerMap.get(headerName);
				httpMethod.setHeader(headerName, headerValue);
			}
		}
		httpMethod.setHeader(HEADER_TRACEID, IdUtil.simpleUUID());
	}

	/**
	 * 设置post的请求参数
	 * @param paramMap 请求参数
	 * @param httpPost HttpPost对象
	 * @param encodingOut 字符编码
	 */
	private static void setRequestParameter(Map<String, String> paramMap, HttpPost httpPost, String encodingOut) throws UnsupportedEncodingException {
		// 封装请求参数
		if(paramMap != null && paramMap.size()>0) {
			List<NameValuePair> paramList = new ArrayList<>(paramMap.size());
			for(Map.Entry<String, String> entry : paramMap.entrySet()) {
				paramList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
			httpPost.setEntity(new UrlEncodedFormEntity(paramList, encodingOut));
		}
	}

	/**
	 * 使用POST方式调用HTTP请求
	 *
	 * @param url 请求地址的URL
	 * @param paramMap 请求的参数列表，必须转化为字符串的键值对
	 * @return 返回响应的结果字符串
	 * @since 1.0.0
	 */
	public static String post(String url, Map<String, String> paramMap) {
		return post(url, paramMap, null, DEFAULT_CHARSET, DEFAULT_CHARSET);
	}

	/**
	 *
	 * @param url 请求地址
	 * @param jsonString 字符串
	 * @return 服务器响应内容
	 */
	public static String post(String url, String jsonString) {
		return post(url, jsonString, null, DEFAULT_CHARSET, DEFAULT_CHARSET);
	}

	/**
	 * 使用POST方式调用HTTP请求
	 *
	 * @param url 请求地址的URL
	 * @param paramMap 请求的参数列表，必须转化为字符串的键值对
	 * @param headerMap 请求的头信息，必须转化为字符串的键值对
	 * @param encodingIn 返回报文编码
	 * @param encodingOut 请求报文编码
	 * @return 返回响应的结果字符串
	 * @since 1.0.0
	 */
	public static String post(String url, Map<String, String> paramMap, Map<String, String> headerMap,
							  String encodingOut, String encodingIn) {
		String result = null;
		CloseableHttpResponse httpResp = null;

		long startTime = System.currentTimeMillis();
		try {
			HttpPost httpPost = new HttpPost(url);
			//设置参数
			setRequestParameter(paramMap, httpPost, encodingOut);
			setRequestHeader(headerMap, httpPost);
			if(LOGGER.isInfoEnabled()) {
				LOGGER.info("[POST] 发起请求[{}]到[{}], 请求头:{}, 请求参数:{},：{}",httpPost.getHeaders(HEADER_TRACEID), url, headerMap, paramMap);
			}
			httpResp = httpClient.execute(httpPost);
			int statusCode = httpResp.getStatusLine().getStatusCode();
			if (statusCode == HttpStatus.SC_OK) {
				result = EntityUtils.toString(httpResp.getEntity(), encodingIn);
				if(LOGGER.isInfoEnabled()) {
					LOGGER.info("[POST] 请求[{}]成功！,耗时:{} 毫秒,返回结果：{}",httpPost.getHeaders(HEADER_TRACEID), System.currentTimeMillis()-startTime, result);
				}
			} else {
				LOGGER.error("[POST] 请求[{}]失败！状态码:{}",httpPost.getHeaders(HEADER_TRACEID),  httpResp.getStatusLine().getStatusCode());
			}
		} catch(Exception e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			try {
				if(httpResp != null) {
					httpResp.close();
				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			}
		}
		return result;
	}

	/**
	 * 使用POST方式调用HTTP请求
	 *
	 * @param url 请求地址的URL
	 * @param requestBody 请求体内容
	 * @param headerMap 请求的头信息，必须转化为字符串的键值对
	 * @return 返回响应的结果字符串
	 * @since 1.0.0
	 */
	public static String post(String url, String requestBody, Map<String, String> headerMap,
							  String encodingOut, String encodingIn) {
		String result = null;
		CloseableHttpResponse httpResp = null;
		HttpPost httpPost = null;
		try {
			long startTime = System.currentTimeMillis();
			// 创建post方法
			httpPost = new HttpPost(url);
			setRequestHeader(headerMap, httpPost);
			httpPost.setEntity(new StringEntity(requestBody, encodingOut));
			if(LOGGER.isInfoEnabled()) {
				LOGGER.info("[POST] 发起请求[{}]到[{}], 请求头:{}, 请求参数:{}",httpPost.getHeaders(HEADER_TRACEID), url, headerMap, requestBody);
			}
			httpResp = httpClient.execute(httpPost);
			int statusCode = httpResp.getStatusLine().getStatusCode();
			if (statusCode == HttpStatus.SC_OK) {
				result = EntityUtils.toString(httpResp.getEntity(), encodingIn);
				if(LOGGER.isInfoEnabled()) {
					LOGGER.info("[POST] 请求[{}]成功！,耗时:{} 毫秒,返回结果：{}",httpPost.getHeaders(HEADER_TRACEID), System.currentTimeMillis()-startTime, result);
				}
			} else {
				LOGGER.error("[POST] 请求[{}]失败！状态码:{}",httpPost.getHeaders(HEADER_TRACEID),  httpResp.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			LOGGER.error("[POST]请求失败，失败原因：" + e.getMessage(), e);
		} finally {
			try {
				if(httpResp != null) {
					httpResp.close();
				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			}
		}
		return result;
	}

	/**
	 * 方法描述： 创建人：slx-pc 创建时间：2014年10月31日 下午4:18:05
	 *
	 * @param url 请求地址
	 * @param encoding 字符编码
	 */
	public static String post(String url, Map<String, Object> params, String encoding) {
		String result = null;
		CloseableHttpResponse httpResp = null;
		try {
			HttpPost httpPost = new HttpPost(url);
			if (params != null && params.size() > 0) {
				List<NameValuePair> paramList = new ArrayList<NameValuePair>(params.size());
				for (Map.Entry<String, Object> entry : params.entrySet()) {
					paramList.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
				}
				httpPost.setEntity(new UrlEncodedFormEntity(paramList, encoding));
			}
			//设置请求和传输超时时间
			httpResp = httpClient.execute(httpPost);
			int statusCode = httpResp.getStatusLine().getStatusCode();
			if (statusCode == HttpStatus.SC_OK) {
				result = EntityUtils.toString(httpResp.getEntity(), encoding);
				LOGGER.info("HttpGet方式请求成功！返回结果：{}", result);
			} else {
				LOGGER.info("HttpGet方式请求失败！状态码:" + httpResp.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			try {
				if (httpResp != null) {
					httpResp.close();
				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			}

		}
		return result;
	}

	/**
	 * 发起post请求
	 * @param url 请求地址
	 * @param paramMap 请求参数
	 * @param encodingIn 返回报文编码
	 * @param encodingOut 请求报文编码
	 */
	public static String post(String url, Map<String, String> paramMap, String encodingIn, String encodingOut) {
		return post(url, paramMap, null, encodingOut, encodingIn);
	}

}
